﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Roll
{
    public enum RollType
    {
        SameByte,
        SmallNum,
        CS,
        CS2,
        C,
        GO,
        JAVA,
        JAVA2,
        UNITY,
        UNITY2,
        SHA256,
        SHA256X2,
        //JAVA,
    }
    public static class RoolMgr
    {
        public interface IRoll
        {
            RollType type
            {
                get;
            }
            byte turn
            {
                get;
            }
            void Roll32(UInt32 seed, byte[] outdata);
        }

        public static IRoll GetRool(RollType type, byte turn)
        {
            switch (type)
            {
                case RollType.SameByte:
                    return new RoolSameByte(turn);
                case RollType.SmallNum:
                    return new RoolSmallnum(turn);
                case RollType.CS:
                    return new RoolCSharp(turn);
                case RollType.CS2:
                    return new RoolCSharp2(turn);
                case RollType.C:
                    return new RoolC(turn);
                case RollType.GO:
                    return new RoolGo(turn);

                case RollType.JAVA:
                    return new RoolJAVA(turn);
                case RollType.JAVA2:
                    return new RoolJAVA2(turn);
                case RollType.UNITY:
                    return new RoolUnity(turn);
                case RollType.UNITY2:
                    return new RoolUnity2(turn);
                case RollType.SHA256:
                    return new RollSha256(turn);
                case RollType.SHA256X2:
                    return new RollSha256_2(turn);
                default:
                    throw new Exception("not support yet.");
            }
        }
    }
    class RoolSameByte : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolSameByte(byte turn)
        {
            this.type = RollType.SameByte;
            this.turn = turn;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            for (var i = 0; i < 32; i++)
            {
                uint v = seed; //%256;
                if (turn > 0) v = (v + turn);// % 256;
                outdata[i] = (byte)v;
            }
        }
    }
    class RoolSmallnum : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolSmallnum(byte turn)
        {
            this.turn = turn;
            this.type = RollType.SmallNum;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_Num(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }
    class RoolCSharp : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolCSharp(byte turn)
        {
            this.turn = turn;
            this.type = RollType.CS;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_CSharp(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }
    class RoolCSharp2 : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolCSharp2(byte turn)
        {
            this.turn = turn;
            this.type = RollType.CS2;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_CSharp2(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }

    class RoolJAVA : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolJAVA(byte turn)
        {
            this.turn = turn;
            this.type = RollType.JAVA;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_CSharp(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }
    class RoolJAVA2 : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolJAVA2(byte turn)
        {
            this.turn = turn;
            this.type = RollType.JAVA2;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_CSharp2(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }

    class RoolUnity : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolUnity(byte turn)
        {
            this.turn = turn;
            this.type = RollType.UNITY;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_CSharp(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }
    class RoolUnity2 : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolUnity2(byte turn)
        {
            this.turn = turn;
            this.type = RollType.UNITY2;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_CSharp2(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }


    class RoolC : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolC(byte turn)
        {
            this.type = RollType.C;
            this.turn = turn;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            //c的范围只有 ffffff 最高位直接拿来turn
            var turn2 = seed / 0x1000000;
            if (turn2 > 0)
            {
                turn = (byte)turn2;
                seed = seed % 0x1000000;
            }

            Roll.Roll32.Roll32_C(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }
    class RoolGo : RoolMgr.IRoll
    {
        public RollType type
        {
            get;
            private set;
        }
        public byte turn
        {
            get;
            private set;
        }
        public RoolGo(byte turn)
        {
            this.turn = turn;
            this.type = RollType.GO;
        }
        public void Roll32(UInt32 seed, byte[] outdata)
        {
            Roll.Roll32.Roll32_Go(seed, outdata);
            if (turn > 0)
            {
                for (var i = 0; i < 32; i++)
                {
                    uint v = (uint)outdata[i] + turn; //%256;
                    outdata[i] = (byte)v;
                }
            }
        }
    }

    class RollSha256 : RoolMgr.IRoll
    {
        public RollSha256(byte turn)
        {
            this.turn = turn;

        }
        public RollType type => RollType.SHA256;

        public byte turn
        {
            get; private set;
        }
        //[ThreadStatic]
        //System.Security.Cryptography.SHA256 sha256;


        [ThreadStatic]
        byte[] hashsrc;
        public void Roll32(uint seed, byte[] outdata)
        {
            //if (sha256 == null)
              using var  sha256 = System.Security.Cryptography.SHA256.Create();
            //sha256.Initialize();

            if (hashsrc == null) hashsrc = new byte[64];
            var buf = BitConverter.GetBytes(seed);
            for (var i = 0; i < 1; i++)
            {
                hashsrc[i * 4 + 0] = buf[0];
                hashsrc[i * 4 + 1] = buf[1];
                hashsrc[i * 4 + 2] = buf[2];
                hashsrc[i * 4 + 3] = buf[3];
            }
            try
            {

                var hash = sha256.ComputeHash(hashsrc);
                for(var i = 0; i < 32;i++)
                {
                    outdata[i] = hash[i];
                }
               // Array.Copy(hash, outdata, 32);
            }
            catch (Exception err)
            {

            }


        }
    }
    class RollSha256_2 : RoolMgr.IRoll
    {
        public RollSha256_2(byte turn)
        {
            this.turn = turn;

        }
        public RollType type => RollType.SHA256X2;

        public byte turn
        {
            get; private set;
        }
        //[ThreadStatic]
        //System.Security.Cryptography.SHA256 sha256;


        [ThreadStatic]
        byte[] hashsrc;
        public void Roll32(uint seed, byte[] outdata)
        {
            //if (sha256 == null)
            using var sha256 = System.Security.Cryptography.SHA256.Create();
            //sha256.Initialize();

            if (hashsrc == null) hashsrc = new byte[64];
            var buf = BitConverter.GetBytes(seed);
            for (var i = 0; i < 1; i++)
            {
                hashsrc[i * 4 + 0] = buf[0];
                hashsrc[i * 4 + 1] = buf[1];
                hashsrc[i * 4 + 2] = buf[2];
                hashsrc[i * 4 + 3] = buf[3];
            }
            try
            {

                var hash = sha256.ComputeHash(hashsrc);
                hash = sha256.ComputeHash(hash);
                for (var i = 0; i < 32; i++)
                {
                    outdata[i] = hash[i];
                }
                // Array.Copy(hash, outdata, 32);
            }
            catch (Exception err)
            {

            }


        }
    }
    //class RoolJava : RoolMgr.IRoll
    //{
    //    public RollType type
    //    {
    //        get;
    //        private set;
    //    }
    //    public byte turn
    //    {
    //        get;
    //        private set;
    //    }
    //    public RoolJava(byte turn)
    //    {
    //        this.turn = turn;
    //        this.type = RollType.JAVA;
    //    }
    //    public void Roll32(UInt32 seed, byte[] outdata)
    //    {
    //        Roll.Roll32.Roll32_Java(seed, outdata);
    //        if (turn > 0)
    //        {
    //            for (var i = 0; i < 32; i++)
    //            {
    //                uint v = (uint)outdata[i] + turn; //%256;
    //                outdata[i] = (byte)v;
    //            }
    //        }
    //    }
    //}
}
